#include "directxwindow.h"
#include "defines.h"
#include <algorithm>
#include <d3dx9.h>


namespace gunsmith
{
IDirect3DDevice9 *CDirectXWindow::m_pDevice = NULL;

CDirectXWindow::CDirectXWindow() : CWindowsWindow(800,600)
{
	Init();
}

CDirectXWindow::~CDirectXWindow()
{
	Destroy();
}

/*!	\brief		Initializes the class.
	\return		void
	\remarks	
	\sa			*/
void CDirectXWindow::Init()
{
	InitDevice();
}

/*!	\brief		Invokes things that need to be done before initialization.
	\return		void
	\remarks	
	\sa			*/
void CDirectXWindow::PreInit()
{
}

/*!	\brief		Releases devices and allocated memory.
	\return		void
	\remarks	
	\sa			*/
void CDirectXWindow::Destroy()
{
	RELEASE(m_pDevice);

	delete [] m_pDisplayModes;
	m_pDisplayModes = NULL;
}

/*!	\brief		Initializes device and retrieves adapter informations. (see remarks)
	\return		void
	\remarks	Invokes dxd3d->GetAdapterIdentifier(), dxd3d->GetDeviceCaps(), GetResolutions(), CreateDevice()
	\sa			GetResolutions(), CreateDevice() */
void CDirectXWindow::InitDevice()
{
	IDirect3D9 *pD3d = Direct3DCreate9(D3D_SDK_VERSION);
	
	// get adapter caps
	pD3d->GetAdapterIdentifier(D3DADAPTER_DEFAULT, 0, &m_xAdapIdent);
	pD3d->GetDeviceCaps(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, &m_xCaps);

	GetResolutions(pD3d);
	CreateDevice(pD3d);

	// get runtime version mode (debug/retail)
	m_bDXInDebugMode = IsDXInDebugMode();

	RELEASE(pD3d);
}

/*!	\brief		Creates the DirectX device.
	\param		pD3d	IDirect3D9 pointer (see remarks)
	\return		void
	\remarks	Doesn't check if pointer is valid.
	\sa			*/
void CDirectXWindow::CreateDevice(IDirect3D9 *pD3d)
{
	D3DPRESENT_PARAMETERS xParams;
	xParams.AutoDepthStencilFormat		= D3DFMT_D24X8;
	xParams.BackBufferCount				= 1;
	xParams.BackBufferFormat			= D3DFMT_A8R8G8B8;
	xParams.BackBufferWidth				= m_iWidth;
	xParams.BackBufferHeight			= m_iHeight;
	xParams.EnableAutoDepthStencil		= true;
	xParams.Flags						= 0;
	xParams.FullScreen_RefreshRateInHz	= 0;
	xParams.hDeviceWindow				= m_hWnd;
	xParams.MultiSampleQuality			= 0;
	xParams.MultiSampleType				= D3DMULTISAMPLE_NONE;
	xParams.PresentationInterval		= D3DPRESENT_INTERVAL_IMMEDIATE;
	xParams.SwapEffect					= D3DSWAPEFFECT_DISCARD;
	xParams.Windowed					= true;

	if(FAILED(pD3d->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, m_hWnd, D3DCREATE_HARDWARE_VERTEXPROCESSING, &xParams, &m_pDevice))) {
		MessageBox(NULL, ERRMSG_CANT_CREATE_DEVICE, ERRCAP_CANT_CREATE_DEVICE, MB_OK | MB_ICONERROR);
		m_bRunning = false;
		return;
	}

	m_pResMngr->RegisterDevice(m_pDevice);
}

/*!	\brief		Sorts the collected adapter modes by resolution.
	\param		pModeA	Adapter Mode A
	\param		pModeB	Adapter Mode B
	\return		int		0 for equal resolutions.
	\remarks	
	\sa			*/
int CDirectXWindow::SortAdapterModes(const void *pModeA, const void *pModeB)
{
	return ((D3DDISPLAYMODE*)pModeA)->Width - ((D3DDISPLAYMODE*)pModeB)->Width;
}

/*!	\brief		Retrieves adapter resolutions.
	\param		pD3d	IDirect3D9 pointer (see remarks)
	\return		void
	\remarks	Doesn't check if pointer is valid.
	\sa			*/
void CDirectXWindow::GetResolutions(IDirect3D9 *pD3d)
{
	UINT iAdapCount = pD3d->GetAdapterModeCount(D3DADAPTER_DEFAULT, D3DFMT_X8R8G8B8);
	m_pDisplayModes = new D3DDISPLAYMODE[iAdapCount];

	// sort resolutions
	for(UINT i = 0; i < iAdapCount; ++i)
		pD3d->EnumAdapterModes(D3DADAPTER_DEFAULT, D3DFMT_X8R8G8B8, i, &m_pDisplayModes[i]);
	std::qsort((void*)m_pDisplayModes, iAdapCount, sizeof(D3DDISPLAYMODE), SortAdapterModes);
}

/*!	\brief		Main render function. (see remarks)
	\param		p_fDelta	Delta time
	\return		void
	\remarks	Invokes functions in the following order: CreateScene(), dxdev->Clear() + dxdev->BeginScene(),
				RenderScene(), DisplayInfo(), dxdev->EndScene() + dxdev->Present()
	\sa			CreateScene(), RenderScene(), DisplayInfo()  */
void CDirectXWindow::Render(float fDelta)
{
	CreateScene();
	RenderScene(fDelta);
}

/*!	\brief		Responsible for scene creation.
	\return		void
	\remarks	
	\sa			*/
void CDirectXWindow::CreateScene()
{
}

/*!	\brief		The scene to be rendered.
	\param		fDelta	Delta time.
	\return		void
	\remarks	
	\sa			*/
void CDirectXWindow::RenderScene(float fDelta)
{
}

/*!	\brief		Method to display FPS or even debug info.
	\return		void
	\remarks	
	\sa			*/
void CDirectXWindow::DisplayInfo(float fDelta)
{
}

/*!	\brief		Retrieve device.
	\return		IDirect3DDevice9*
	\remarks	
	\sa			*/
IDirect3DDevice9* CDirectXWindow::GetDevice()
{
	return m_pDevice;
}

/*!	\brief		Returns wether DirectX is set to Debug or Retail mode.
	\return		bool	Returns true for Debug mode.
	\remarks	
	\sa			*/
bool CDirectXWindow::IsDXInDebugMode() const
{
	HKEY hkCU, hkLoc;
	long lResultCU = RegOpenCurrentUser(KEY_READ, &hkCU);
	long lResultLoc = RegOpenKey(hkCU, "Software\\Microsoft\\Direct3D\\ControlPanel", &hkLoc);

	DWORD dwData, dwType;
	DWORD dwSize = sizeof(DWORD);

	long lResult = RegQueryValueEx(hkLoc, "LoadDebugRuntime", NULL, &dwType, (LPBYTE)&dwData, &dwSize);

	RegCloseKey(hkLoc);
	RegCloseKey(hkCU);

	return dwData > 0 ? true : false;
}
};